﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using System.Web;

namespace Social.Facebook
{
	public class FacebookGraph : ISocialNetwork
	{
		private readonly IFacebookGraphConfig config;

		public FacebookGraph(IFacebookGraphConfig fbgConfig)
		{
			this.config = fbgConfig;
		}

		#region ISocialNetwork Members

		public void Authenticate()
		{
			if (!string.IsNullOrEmpty(config.AuthenticationCode))
			{
				var token = GraphApi.GetToken(config.AppKey, config.SecreteKey, config.AuthenticationCode, config.RedirectUrl);
				config.AccessToken = token;
				config.SaveAccessToken();
			}
			else
			{
				config.AccessToken = "";
			}
		}

		public string ConnectUrl
		{
			get 
			{
				return GraphApi.CreateLoginUrl(config.AppKey, config.RedirectUrl, config.permissions);
			}
		}

		public bool IsAuthenticated
		{
			get 
			{
				return !string.IsNullOrEmpty(config.AccessToken);
			}
		}

		public void Login()
		{
			HttpContext.Current.Response.Redirect(ConnectUrl);
		}

		#endregion
	}


	public class GraphApi
	{
		public const string ConnectUrlFormat = "https://graph.facebook.com/oauth/authorize?display=popup&client_id={0}&scope={1}&redirect_uri={2}";
		public const string TokenUrlFormat = "https://graph.facebook.com/oauth/access_token?client_id={0}&redirect_uri={1}&client_secret={2}&code={3}";
		public const string UserInfoUrlFormat = "https://graph.facebook.com/{0}?access_token={1}";
		public const string CurrentUserINfoUrlFormat = "https://graph.facebook.com/me?access_token={0}";


		//the names of the permissions that can be requested in the query string. when both are requested they should be delimited by a comma
		private const string OFFLINE_ACCESS_PERMISSION_NAME = "offline_access";
		private const string PUBLISH_STREAM_PERMISSION_NAME = "publish_stream";
		private const string READ_STREAM_PERMISSION_NAME = "read_stream";
		private const string CREATE_EVENT_PERMISSION_NAME = "create_event";
		private const string RSVP_EVENT_PERMISSION_NAME = "rsvp_event";
		private const string READ_MAILBOX_PERMISSION_NAME = "read_mailbox";
		private const string MANAGE_PAGES_PERMISSION_NAME = "manage_pages";


		public string Token { get; set; }


		public static string CreateLoginUrl(string apiKey, string redirectUrl, params FacebookExtededPermissions[] permissions)
		{
			return string.Format(ConnectUrlFormat, apiKey, GetPermissions(permissions), redirectUrl);
		}

		private static string GetPermissions(params FacebookExtededPermissions[] args) //bool askForOfflineAccess, bool askForPublishStream)
		{
			if (args == null || args.Length == 0)
				return "";

			StringBuilder sb = new StringBuilder();
			foreach (var permission in args)
			{
				switch (permission)
				{
					case FacebookExtededPermissions.OffileAccess: sb.Append(OFFLINE_ACCESS_PERMISSION_NAME); break;
					case FacebookExtededPermissions.PublishStream: sb.Append(PUBLISH_STREAM_PERMISSION_NAME); break;
					case FacebookExtededPermissions.ManagePages : sb.Append(MANAGE_PAGES_PERMISSION_NAME); break;
					default:
						break;
				}

				sb.Append(",");
			}
			if (sb.Length > 0)
				sb.Remove(sb.Length - 1, 1);
			return sb.ToString();
		}

		public static string GetToken(string apiKey, string secreteKey, string Code, string redirectUrl)
		{
			var response = WebReq.GetWebResponse(string.Format(TokenUrlFormat, apiKey, redirectUrl, secreteKey, Code), 30000, false);
			response = response.Substring(response.IndexOf("=") + 1);

			return response;
		}

		public static ContentServiceResponse<User> GetUserByID(long userId)
		{
			var response = new ContentServiceResponse<User>();
			try
			{
				string user = WebReq.GetWebResponse("https://graph.facebook.com/" + userId, 30000, false, null, null, null);
				JavaScriptSerializer jss = new JavaScriptSerializer();
				response.Content = jss.Deserialize<User>(user);
				response.ResultOption = ServiceResultOptions.Success;
			}
			catch (Exception ex)
			{
				response.SetException(ex);
			}

			return response;
		}

		public static ContentServiceResponse<Page> GetPageById(long pageID)
		{
			var response = new ContentServiceResponse<Page>();
			try
			{
				string page = WebReq.GetWebResponse("https://graph.facebook.com/" + pageID.ToString(), 30000, false, null, null, null);
				JavaScriptSerializer jss = new JavaScriptSerializer();
				response.Content = jss.Deserialize<Page>(page);
				response.ResultOption = ServiceResultOptions.Success;
			}
			catch (Exception ex)
			{
				response.SetException(ex);
			}

			return response;
		}

		public static ContentServiceResponse<Page> GetPageByName(string pageName)
		{
			var response = new ContentServiceResponse<Page>();
			try
			{
				string page = WebReq.GetWebResponse("https://graph.facebook.com/" + pageName, 30000, false, null, null, null);
				JavaScriptSerializer jss = new JavaScriptSerializer();
				response.Content = jss.Deserialize<Page>(page);
				response.ResultOption = ServiceResultOptions.Success;
			}
			catch (Exception ex)
			{
				response.SetException(ex);
			}

			return response;
		}

		/// <summary>
		/// Retrieves the accounts administrated by the current user.
		/// The account include pages and applications.
		/// </summary>
		/// <param name="userAccessToken"></param>
		/// <returns></returns>
		public static ContentServiceResponse<List<Account>> GetUserAccounts(string userAccessToken)
		{
			var response = new ContentServiceResponse<List<Account>>();
			try
			{
				string page = WebReq.GetWebResponse("https://graph.facebook.com/me/accounts?access_token=" + userAccessToken, 30000, false, null, null, null);
				JavaScriptSerializer jss = new JavaScriptSerializer();
				response.Content = jss.Deserialize<DataGroup<Account>>(page).data;
				response.ResultOption = ServiceResultOptions.Success;
			}
			catch (Exception ex)
			{
				response.SetException(ex);
			}

			return response;
		}


		public static ContentServiceResponse<User> GetCurrentUser(string token)
		{
			var response = new ContentServiceResponse<User>();
			try
			{
				var url = string.Format(CurrentUserINfoUrlFormat, token);
				string user = WebReq.GetWebResponse(url, 30000, false, null, null, null);
				JavaScriptSerializer jss = new JavaScriptSerializer();
				response.Content = jss.Deserialize<User>(user);
				response.ResultOption = ServiceResultOptions.Success;
			}
			catch (Exception ex)
			{
				response.SetException(ex);
			}

			return response;
		}

		public static ContentServiceResponse<string> PostToFeed(string token, string uid, string text)
		{
			var response = new ContentServiceResponse<string>();
			try
			{
				if (string.IsNullOrEmpty(uid))
					uid = "me";

				string urlFormat = "https://graph.facebook.com/{0}/feed?access_token={1}";
				var url = string.Format(urlFormat, uid, token);
				string data = string.Format("access_token={0}&message={1}", token, text);
				//if (!string.IsNullOrEmpty(linkText))
				//    data += string.Format("&link={0}&name={1}&caption=Caption+for+the+link&description=Longer+description+of+the+link", linkUrl, linkText);                         

				string result = WebReq.GetWebResponse(url, 30000, false, null, null, data);
				JavaScriptSerializer jss = new JavaScriptSerializer();
				response.Content = jss.Deserialize<Ids>(result).id;
				response.SetSuccess();
				return response;

			}
			catch (Exception ex)
			{
				response.SetException(ex);
				return response;
			}
		}

		public static ContentServiceResponse<string> PostLinkToFeed(string token, string uid, string linkUrl, string name, string caption, string description, string message)
		{
			var response = new ContentServiceResponse<string>();
			try
			{
				if (string.IsNullOrEmpty(uid))
					uid = "me";

				string urlFormat = "https://graph.facebook.com/{0}/feed?access_token={1}";
				var url = string.Format(urlFormat, uid, token);
				string data = "link=" + linkUrl;
				//if (!string.IsNullOrEmpty(linkText))
				//    data += string.Format("&link={0}&name={1}&caption=Caption+for+the+link&description=Longer+description+of+the+link", linkUrl, linkText);                         

				string result = WebReq.GetWebResponse(url, 30000, false, null, null, data);
				JavaScriptSerializer jss = new JavaScriptSerializer();
				response.Content = jss.Deserialize<Ids>(result).id;
				response.SetSuccess();
				return response;

			}
			catch (Exception ex)
			{
				response.SetException(ex);
				return response;
			}
		}


		public static ContentServiceResponse<Post> GetPost(string postID)
		{
			var response = new ContentServiceResponse<Post>();
			try
			{
				var url = string.Format("https://graph.facebook.com/{0}", postID);
				string data = WebReq.GetWebResponse(url, 30000, false, null, null, null);
				JavaScriptSerializer jss = new JavaScriptSerializer();
				response.Content = jss.Deserialize<Post>(data);
				response.ResultOption = ServiceResultOptions.Success;
			}
			catch (Exception ex)
			{
				response.SetException(ex);
			}

			return response;
		}
		
		public static ContentServiceResponse<List<Comment>> GetComments(string postID)
		{
			var response = new ContentServiceResponse<List<Comment>>();
			try
			{
				var url = string.Format("https://graph.facebook.com/{0}/comments", postID);
				string page = WebReq.GetWebResponse(url, 30000, false, null, null, null);
				JavaScriptSerializer jss = new JavaScriptSerializer();
				response.Content = jss.Deserialize<DataGroup<Comment>>(page).data;
				response.ResultOption = ServiceResultOptions.Success;
			}
			catch (Exception ex)
			{
				response.SetException(ex);
			}

			return response;
		}


		public class Feed
		{
			public List<Ids> data { get; set; }
		}

		public class Ids
		{
			public string id { get; set; }
		}

		public class Comment
		{
			public string id { get; set; }
			public long fromid { get; set; }
			public string message { get; set; }
			public long time { get; set; }
			public DateTime dateTime
			{
				get { return ConvertTimestamp(time); }
			}

			/// <summary>
			/// method for converting a UNIX timestamp to a regular
			/// System.DateTime value 
			/// </summary>
			/// <param name="timestamp">value to be converted</param>
			/// <returns>converted DateTime in string format</returns>
			public static DateTime ConvertTimestamp(double timestamp)
			{
				//create a new DateTime value based on the Unix Epoch
				DateTime converted = new DateTime(1970, 1, 1, 0, 0, 0, 0);

				//add the timestamp to the value
				DateTime newDateTime = converted.AddSeconds(timestamp);

				//return the value in string format
				return newDateTime;
			}
		}

		public class Post
		{
			public string id { get; set; }
			public int likes { get; set; }
			public string message { get; set; }
			public string picture { get; set; }
			public string link { get; set; }
			public string name { get; set; }
			public string caption { get; set; }
			public string description { get; set; }
			public string source { get; set; }
			public string icon { get; set; }
			public string attribution { get; set; }
			public DataGroup<Comment> comments { get; set; }
			//public string created_time { get; set; }
			//public string updated_time { get; set; }

		}

		public class User
		{
			public long id { get; set; }
			public string first_name { get; set; }
			public string last_name { get; set; }
			public string name { get; set; }
		}

		public class Like
		{
			public long user_id { get; set; }
		}

		public class Page
		{
			public long id { get; set; }
			public string name { get; set; }
			public string picture { get; set; }
			public string category { get; set; }
			public int likes { get; set; }
			public string link { get; set; }
		}

		public class Account
		{
			public long id { get; set; }
			public string name { get; set; }
			public string category { get; set; }
			public string access_token { get; set; }
		}
			 

		public class DataGroup<T>
		{
			public List<T> data;
		}


		
	}

	public enum FacebookExtededPermissions
	{
		OffileAccess,
		PublishStream,
		ManagePages
	}
}
